package today.belief.mole.util;

import java.util.Arrays;

/**
 * @Author LXH
 * @Date 2022/1/17
 * @Description 用位标记十进制数
 */
public class LinkedMarkSet {

    /**
     * long类型存储为8个字节，占位64
     */
    private static final int BIT_SIZE = 64;

    /**
     * 理论上可以标记的数的最大值
     */
    public static final long MAX = (long) Integer.MAX_VALUE * BIT_SIZE - 1;

    private int length;

    private long[] data;

    public LinkedMarkSet() {
        length = 1;
        data = new long[length];
    }

    /**
     * 建议使用想要标记的十进制数的最大值初始化，避免过多的变更数组长度；
     * 也可以不使用最大值来初始化
     */
    public LinkedMarkSet(long max) {
        if (max > MAX || max < 0) {
            throw new RuntimeException("num range: 0 ~ 137438953407");
        }
        length(max);
        data = new long[length];
    }

    /**
     * 实际可标记的数的数量
     */
    private void length(long max) {
        length = index(max) + 1;
    }

    /**
     * 被标记的数在数组中的位置
     */
    private int index(long num) {
        return (int) (num >> 6);
    }

    /**
     * 实际可标记的最大值
     */
    public long max() {
        return (long) BIT_SIZE * length - 1;
    }

    /**
     * range: 0 ~ 137438953407
     */
    public void mark(long num) {
        if (num > MAX || num < 0) {
            throw new RuntimeException("num range: 0 ~ 137438953407");
        }
        if (num > max()) {
            length(num);
            data = Arrays.copyOf(data, length);
        }
        int index = index(num);
        data[index] |= 1L << num;
    }

    /**
     * num range: 0 ~ 137438953407
     */
    public void mark(long from, long to) {
        if (from < 0 || to > MAX || from > to) {
            throw new RuntimeException("num range: 0 ~ 137438953407");
        }
        if (to > max()) {
            length(to);
            data = Arrays.copyOf(data, length);
        }
        for (long i = from; i <= to; i++) {
            int index = index(i);
            data[index] |= 1L << i;
        }
    }

    /**
     * range: 0 ~ 137438953407
     */
    public void flip(long num) {
        if (num > MAX || num < 0) {
            throw new RuntimeException("num range: 0 ~ 137438953407");
        }
        if (num > max()) {
            return;
        }
        int index = index(num);
        data[index] ^= 1L << num;
    }

    /**
     * range: 0 ~ 137438953407
     */
    public void flip(long from, long to) {
        if (from < 0 || to > MAX || from > to) {
            throw new RuntimeException("num range: 0 ~ 137438953407");
        }
        if (to > max()) {
            length(to);
            data = Arrays.copyOf(data, length);
        }
        for (long i = from; i <= to; i++) {
            int index = index(i);
            data[index] ^= 1L << i;
        }
    }

    /**
     * 获取当前数被标记的状态：true-已经被标记，false-未被标记
     */
    public boolean get(long num) {
        if (num > MAX || num < 0) {
            throw new RuntimeException("num range: 0 ~ 137438953407");
        }
        int index = index(num);
        return index < length && (data[index] & (1L << num)) != 0;
    }
}
